package net.lovesrc.allin.biz.idcard;

import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.zhouzhou.fastjava.BizException;
import com.zhouzhou.fastjava.util.FileKit;
import net.lovesrc.allin.biz.BaseService;
import net.lovesrc.allin.biz.FileService;
import net.lovesrc.allin.biz.ocr.OcrService;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.SystemUtils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;

@Service
public class IdCardService extends BaseService {

    public static final File ROOT;

    public static final File TMP_ROOT;

    static {
        if (SystemUtils.IS_OS_LINUX) {
            ROOT = new File("/data/idcard-files");
            TMP_ROOT = new File("/tmp/idcard-files");
        } else {
            ROOT = new File("E:/data/idcard-files");
            TMP_ROOT = new File("E:/tmp/idcard-files");
        }
    }

    @Autowired
    private OcrService ocrService;

    public static final List<String> ACCEPT_EXTENSIONS = Lists.newArrayList("jpg", "png", "JPG", "PNG", "jpeg", "JPEG");

    public void save(String realName,
                     String idCardNumber,
                     MultipartFile file1,
                     MultipartFile file2,
                     MultipartFile file3
    ) throws IOException {
        checkNotBlank("realName", realName);
        checkNotBlank("idCardNumber", idCardNumber);
        JSONObject row = queryForRow(jdbcTemplate, "select * from t_id_card where idCardNumber = ?", idCardNumber);
        if (row != null) {
            saveToLocal(file1, idCardNumber, "1");
            saveToLocal(file2, idCardNumber, "2");
            saveToLocal(file3, idCardNumber, "3");
            doUpdate(realName, idCardNumber);
        } else {
            if (file1 == null || file1.isEmpty()) {
                throw BizException.messageOf("请上传身份证正面照");
            }
            if (file2 == null || file2.isEmpty()) {
                throw BizException.messageOf("请上传身份证反面照");
            }
            if (file3 == null || file3.isEmpty()) {
                throw BizException.messageOf("请上传身份证手持照");
            }
            saveToLocal(file1, idCardNumber, "1");
            saveToLocal(file2, idCardNumber, "2");
            saveToLocal(file3, idCardNumber, "3");
            doInsert(realName, idCardNumber);
        }
    }

    private void doInsert(String realName, String idCardNumber) {
        JSONObject newRow = new JSONObject()
                .fluentPut("idCardNumber", idCardNumber)
                .fluentPut("realName", realName);
        insertRow(jdbcTemplate, "t_id_card", newRow);
    }

    private void doUpdate(String realName, String idCardNumber) {
        jdbcTemplate.update("update t_id_card set realName = ? where idCardNumber = ?", realName, idCardNumber);
    }

    private void saveToLocal(MultipartFile file, String idCardNumber, String filename) throws IOException {
        if (file.isEmpty()) {
            return;
        }
        String extension = FilenameUtils.getExtension(file.getOriginalFilename());
        String newFilename = filename + "." + extension;
        File dir = new File(IdCardService.ROOT, idCardNumber);
        if (!dir.exists()) {
            dir.mkdirs();
        }
        File newFile = new File(dir, newFilename);
        if (newFile.exists()) {
            newFile.renameTo(new File(dir, System.currentTimeMillis() + "-" + filename + "." + extension));
        }
        FileOutputStream fos = new FileOutputStream(newFile);
        IOUtils.copy(file.getInputStream(), fos);
        fos.close();
    }

    public void ocr() throws IOException {
        List<JSONObject> rows = queryForRows(jdbcTemplate, "select * from t_id_card where frontOcrStatus = 'no_ocr' order by file1Bytes limit 5");

        for (JSONObject row : rows) {
            String idCardNumber = row.getString("idCardNumber");
            execute("IdCardOcr", idCardNumber, () -> {
                try {
                    doOcr(idCardNumber);
                } catch (IOException e) {
                    logger.error("doOcr Error", e);
                }
            });
        }
    }

    private void doOcr(String idCardNumber) throws IOException {
        File dir = new File(ROOT, idCardNumber);
        String frontOcrStatus;
        {
            // 正面
            File image = FileKit.tryGetFile(dir, "1", IdCardService.ACCEPT_EXTENSIONS);
            JSONObject resJson = ocrService.idCard(image, "front");
            jdbcTemplate.update("update t_id_card set frontResText = ? where idCardNumber = ?", resJson.toJSONString(), idCardNumber);
            frontOcrStatus = resJson.getString("image_status");
            try {
                JSONObject wordsResult = resJson.getJSONObject("words_result");
                String address = wordsResult.getJSONObject("住址").getString("words");
                String ocrIdCardNumber = wordsResult.getJSONObject("公民身份号码").getString("words");
                String birthday = wordsResult.getJSONObject("出生").getString("words");
                String ocrRealName = wordsResult.getJSONObject("姓名").getString("words");
                String gender = wordsResult.getJSONObject("性别").getString("words");
                String nation = wordsResult.getJSONObject("民族").getString("words");
                JSONObject sets = new JSONObject()
                        .fluentPut("frontOcrStatus", frontOcrStatus)
                        .fluentPut("frontOcrTime", new Date())
                        .fluentPut("address", address)
                        .fluentPut("ocrIdCardNumber", ocrIdCardNumber)
                        .fluentPut("birthday", birthday)
                        .fluentPut("ocrRealName", ocrRealName)
                        .fluentPut("gender", gender)
                        .fluentPut("nation", nation);
                updateRow(jdbcTemplate, "t_id_card", sets, "idCardNumber = ?", idCardNumber);
            } catch (Exception e) {
                jdbcTemplate.update("update t_id_card set frontOcrStatus = ? where idCardNumber = ?", frontOcrStatus, idCardNumber);
                // over
                return;
            }
        }
        String backOcrStatus;
        {
            // 反面
            File image = FileKit.tryGetFile(dir, "2", IdCardService.ACCEPT_EXTENSIONS);
            JSONObject resJson = ocrService.idCard(image, "back");
            jdbcTemplate.update("update t_id_card set backResText = ? where idCardNumber = ?", resJson.toJSONString(), idCardNumber);
            backOcrStatus = resJson.getString("image_status");
            try {
                JSONObject wordsResult = resJson.getJSONObject("words_result");
                String issuingAuthority = wordsResult.getJSONObject("签发机关").getString("words");
                String expiryStartDate = wordsResult.getJSONObject("签发日期").getString("words");
                String expiryEndDate = wordsResult.getJSONObject("失效日期").getString("words");
                JSONObject sets = new JSONObject()
                        .fluentPut("backOcrStatus", backOcrStatus)
                        .fluentPut("backOcrTime", new Date())
                        .fluentPut("issuingAuthority", issuingAuthority)
                        .fluentPut("expiryStartDate", expiryStartDate)
                        .fluentPut("expiryEndDate", expiryEndDate);
                updateRow(jdbcTemplate, "t_id_card", sets, "idCardNumber = ?", idCardNumber);
            } catch (Exception e) {
                jdbcTemplate.update("update t_id_card set backOcrStatus = ? where idCardNumber = ?", backOcrStatus, idCardNumber);
            }
        }
    }

    public File safeToNormalSizeFile(File imageFile) {
        if (imageFile.length() < 500 * FileUtils.ONE_KB) {
            return imageFile;
        }
        try {
            String filename = imageFile.getName();
            String extension = FilenameUtils.getExtension(filename);
            BufferedImage image = ImageIO.read(imageFile);
            int newWidth;
            int newHeight;
            if (image.getWidth() > image.getHeight()) {
                newWidth = 800;
                newHeight = (int) Math.round(image.getHeight() * newWidth * 1.0 / image.getWidth());
            } else {
                newHeight = 800;
                newWidth = (int) Math.round(image.getWidth() * newHeight * 1.0 / image.getHeight());
            }
            Image tmp = image.getScaledInstance(newWidth, newHeight, Image.SCALE_DEFAULT);
            BufferedImage result = new BufferedImage(
                    newWidth,
                    newHeight,
                    BufferedImage.TYPE_INT_ARGB);
            Graphics2D g2 = result.createGraphics();
            g2.drawImage(tmp, 0, 0, null);
            g2.dispose();
            File target = new File(FileService.OUT_TEMP_ROOT, UUID.randomUUID().toString() + "." + extension);
            ImageIO.write(result, extension, target);
            return target;
        } catch (Exception e) {
            logger.error("toNormalSize error", e);
            sendNotificationToAdmin(e);
            return imageFile;
        }
    }

}
